﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace Eventbrite.LinqProvider
{
    public class QueryProvider: IQueryProvider {
       
        IDataReader _reader;
        MethodInfo _readMethod;

        public QueryProvider(IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentException("reader");

            _reader = reader;
            _readMethod = _reader.GetType().GetMethod("Read");
        }
             
        public IQueryable<T> CreateQuery<T>(Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentException("Argument expression is not valid");
            }
            return new EventbriteQuery<T>(this, expression);
        }
       
        public IQueryable CreateQuery(Expression expression)
        {
            Type elementType = Helpers.TypeSystem.GetElementType(expression.Type);
            return (IQueryable)Activator.CreateInstance(typeof(EventbriteQuery<>).MakeGenericType(elementType), new object[] { this, expression });
        }

        public TResult Execute<TResult>(Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            
            var result = this.Execute(expression);
           
            // make sure that we are returning the correct object
            if ( Helpers.TypeSystem.GetElementType(expression.Type) == expression.Type)
            {
                // if it is Find, we have to return single object
                return result != null ? ( (IEnumerable<TResult>) result).ToArray()[0] : default(TResult);  
            }

            return (TResult)result;
        }

        public object Execute(Expression expression)
        {
            Type elementType = Helpers.TypeSystem.GetElementType(expression.Type);
            expression =  Helpers.Evaluator.PartialEval(expression);
            var request = GetUrlRequest(expression);
            return _readMethod.MakeGenericMethod(elementType).Invoke(_reader, new[] { request });
        }

        public RequestInfo GetUrlRequest(Expression expression)
        {
            var translator = new Translators.QueryTranslator();

            return translator.Translate(expression);
        }
    }
   
}
